using System;
using System.IO;
using System.Web;
using System.Net;
using System.Text;
using System.Diagnostics ;
using System.Collections;
using Microsoft.SharePoint;
using System.Globalization;
using System.Threading;
using SharePoint.ImportExport.Core;
using System.Configuration;

namespace SharePoint2003Import
{
	/// <summary>
	/// Summary description for FrontpageRPC.
	/// </summary>
	internal class FrontpageRPC
	{
		
		private static System.IFormatProvider format =null;
			
		private static string serverExtensionsVersion = null;

		private static int TimeOut=-1;
		


		private static string sFPExtensionPath = "/_vti_bin/_vti_aut/author.dll";
		//Retrieve the version of the Front Page Server Extensions.
		//The version is needed for formulating the put documents method call.
		private static string GetServerExtensionsVersion(string sURL)
		{
			if(-1==TimeOut)
				TimeOut =System.Convert.ToInt32(ConfigurationSettings.AppSettings.Get("TimeOut"));
			if(null != serverExtensionsVersion) return serverExtensionsVersion ;
			//Use the US locale for the FrontPage RPC calls
			format=new System.Globalization.CultureInfo("en-US", true);
			
			HttpWebRequest request = (HttpWebRequest)WebRequest.Create((sURL + sFPExtensionPath));

			// FP-RPC Method "server version"
			string sPostData = "method=server version:6.0.2.5614&service_name=/";
			byte[] postDataBytes = System.Text.Encoding.ASCII.GetBytes(sPostData);
		
			//Set headers 
			request.Credentials  = CredentialCache.DefaultCredentials;
			request.Method = "POST";
			request.ContentType = "application/x-www-form-urlencoded"; 
			request.ContentLength = postDataBytes.Length ;
			request.Headers.Add("X-Vermeer-Content-Type","application/x-www-form-urlencoded"); 
			Stream newStream =null;
			
			try
			{
				//Write the post data to request stream
				newStream = request.GetRequestStream() ;
				newStream.Write(postDataBytes,0,postDataBytes.Length);
				

				//Send the request and get response
				HttpWebResponse response = (HttpWebResponse)request.GetResponse ();
				
				//Read the response
				Stream responseStream = response.GetResponseStream();
				using(StreamReader reader = new StreamReader(responseStream))
				{
				 
					serverExtensionsVersion = ExtractServerExtensionsVersion(reader.ReadToEnd()); 
					Debug.Assert(null!=serverExtensionsVersion,"FATAL ERROR: ServerExtensionsVersion is null!!"); 
					if(null == serverExtensionsVersion)
					{
						Logger.Log(ErrorLevel.Error,"Could not retrieve the server extension version");
					}
				}
				
			}
			catch(WebException Ex)
			{
				Logger.Log(ErrorLevel.Error,"Could not retrieve the server extension version. Error: "+Ex.Message );
				return null;
			}
			catch(IOException Ex)
			{
				Logger.Log(ErrorLevel.Error,"Could not retrieve the server extension version. Error: "+Ex.Message );
				return null;
			}
			finally
			{
				if(null!=newStream)
					newStream.Close();
			}
			return serverExtensionsVersion;
		}

		//Helper method for GetServerExtensionsVersion()
		//The response from the server is in the format :
		//<html><head><title>vermeer RPC packet</title></head>\n<body>\n
		//<p>method=server version:6.0.0.0\n
		//<p>server version=\n<ul>\n
		//<li>major ver=6\n <li>minor ver=0\n<li>phase ver=2\n<li>ver incr=5528\n
		//</ul>\n<p>source control=1\n
		//</body>\n</html>\n
		private static string ExtractServerExtensionsVersion(string response)
		{
			const string majorVer = "major ver";
			const string minorVer = "minor ver";
			const string phaseVer = "phase ver";
			const string verIncr = "ver incr";

			StringBuilder bldr = new StringBuilder();
			int index = response.IndexOf(majorVer) ;
			if (-1 == index) return null;
			int startIndex = index + majorVer.Length + 1;
			int endIndex = response.IndexOf("<",index);
			bldr.Append(response.Substring(startIndex,endIndex-startIndex).Trim());
			bldr.Append("."); 

			index = response.IndexOf(minorVer, index) ;
			if (-1 == index) return null;
			startIndex = index + minorVer.Length + 1;
			endIndex = response.IndexOf("<",index);
			bldr.Append(response.Substring(startIndex,endIndex-startIndex).Trim());
			bldr.Append("."); 

			index = response.IndexOf(phaseVer, index) ;
			if (-1 == index) return null;
			startIndex = index + phaseVer.Length + 1;
			endIndex = response.IndexOf("<",index);
			bldr.Append(response.Substring(startIndex,endIndex-startIndex).Trim());
			bldr.Append("."); 
 
			index = response.IndexOf(verIncr, index) ;
			if (-1 == index) return null;
			startIndex = index + verIncr.Length + 1;
			endIndex = response.IndexOf("<",index);
			bldr.Append(response.Substring(startIndex,endIndex-startIndex).Trim());

			return bldr.ToString();
		}
		private static string EncodeStringForFPRequest(string str)
		{
			string encodedString = String.Empty;
			char[] escapeChars = {'\\',';','=',']','['};
	
			for(int i=0;i<str.Length;i++)
			{
				if(-1 != str[i].ToString().IndexOfAny(escapeChars))
				{
					encodedString += System.Web.HttpUtility.UrlEncode("\\" + str[i].ToString()); //"\\" + str[i].ToString();
				}
				else //if(str[i] == encodeChars[0] || str[i] == encodeChars[1])
				{
					encodedString += System.Web.HttpUtility.UrlEncode(str[i].ToString()); 
				}
			}

			return encodedString;
		}
		//Generates the document and meta-data portion of the put documents method call.
		//Helper method for the PutDocuments() method.
		private static string GetDocumentRequestString(DocumentVersion document,string folderUrl, string mimeBoundary, ArrayList listFields)
		{

			string STRING_TYPE = "SW";
			string DATETIME_TYPE = "TW";
			string FP_DATE_FORMAT = "dd MMM yyyy HH':'mm':'ss '0000'";

			StringBuilder bldr = new StringBuilder();

			bldr.Append("\r\n" + mimeBoundary + "\r\n");
			bldr.Append("Content-Type: application/x-www-form-urlencoded" + "\r\n" + "\r\n");
			
			bldr.Append("document=[document_name="); 
			bldr.Append(EncodeStringForFPRequest(folderUrl) + "/"+ EncodeStringForFPRequest(document.Name) + ";"); 
			bldr.Append("meta_info=[");

			DateTime dt;
			
			//Modified By
			bldr.Append("vti_modifiedby" + ";" + STRING_TYPE + "|"); 
			bldr.Append(EncodeStringForFPRequest(document.ModifiedBy)); 

			//Modified On
			dt = document.Modified;

			string modifiedOn = dt.ToString(FP_DATE_FORMAT,format);// 
			bldr.Append(";");
			bldr.Append("vti_timelastmodified" + ";" + DATETIME_TYPE + "|"); 
			bldr.Append(modifiedOn); 

			//title
			if(null != document.Name && document.Name.Length>0)
			{
				bldr.Append(";");
				bldr.Append("vti_title" + ";" + STRING_TYPE + "|");	
				bldr.Append(EncodeStringForFPRequest(document.Name));
			}

			//description
			if(null != document.Description && !(document.Description.Length==0))
			{
				bldr.Append(";");
				bldr.Append("SPSdescription" + ";" + STRING_TYPE + "|");	
				bldr.Append(EncodeStringForFPRequest(document.Description));
			}

			//Owner
			if(null != document.Owner && !(document.Owner.Length==0))
			{
				bldr.Append(";");
				bldr.Append("Owner" + ";" + STRING_TYPE + "|");	
				bldr.Append(EncodeStringForFPRequest(document.Owner)); 
			}

			//Comments
			if(null != document.Comments && !(document.Comments.Length==0))
			{
				bldr.Append(";");
				bldr.Append("vti_sourcecontrolcheckincomment" + ";" + STRING_TYPE + "|"); 
				bldr.Append(EncodeStringForFPRequest(document.Comments));	
			}

			if(null != (document as Document))
			{
				//Created by
				string CreatedBy = EncodeStringForFPRequest(document.CreatedBy);
				bldr.Append(";");
				bldr.Append("vti_author" + ";" + STRING_TYPE + "|"); 
				bldr.Append(CreatedBy); 

				//Created on
				dt = document.Created;
				string createdOn = dt.ToString(FP_DATE_FORMAT,format);// 
				bldr.Append(";");
				bldr.Append("vti_timecreated" + ";" + DATETIME_TYPE + "|");
				bldr.Append(createdOn); 
			}

			//Append all other meta data info for document here
			foreach(string key in document.MetaData.Keys)
			{
				string keyName = Miscellaneous.GetInternalFieldName(listFields,key);     

				if(null == keyName || keyName.Length==0				)
				{
					Logger.Log(ErrorLevel.Warning,"Skipping field-name " + key + ". Field was not defined in List Templates.");  
					continue;
				}
				//VELI CHANGE
				if("description" == keyName)
				{
					keyName = "SPSdescription";
				}

				keyName = EncodeStringForFPRequest(keyName);

				bldr.Append(";");
				bldr.Append(keyName + ";");
				string type=(string)document.MetaData[key];// STRING_TYPE ;
				int nPos=type.IndexOf("-");
				string fieldValue=type.Substring(nPos+1);
				type=type.Substring(0,nPos).ToLower(System.Threading.Thread.CurrentThread.CurrentCulture);
				switch(type)
				{
					case "number":
						type="IX";
						break;
					case "boolean":
						type="BR";
						break;
					case "datetime":
					{
						DateTime dt2=DateTime.Now;
						try
						{
							dt2=Convert.ToDateTime(fieldValue,Importer.Format);
						}
						catch(FormatException fEx)
						{
							Logger.Log(ErrorLevel.NoLog,"Could not resolve the date to the locale specified in the struct.xml file, Error:"+fEx.Message+" trying the default conversion");
							dt2=Convert.ToDateTime(fieldValue);
						}
						fieldValue = dt2.ToString(FP_DATE_FORMAT,format);// 
						type=DATETIME_TYPE;
			
						break;
					}
					default:
						type=STRING_TYPE;
						fieldValue=EncodeStringForFPRequest(fieldValue);
						break;

				}
				bldr.Append(type);
				
				bldr.Append("|"); 
				bldr.Append(fieldValue);
			}

			
			bldr.Append("]]" + "\n"); 
			bldr.Append("\r\n" + mimeBoundary + "\r\n");
			bldr.Append("Content-Type: application/octet-stream" + "\r\n" + "\r\n"); 

			return bldr.ToString(); 
		}
		
		/// <summary>
		/// 
		/// </summary>
		/// <param name="webUrl"></param>
		/// <param name="sharepointFolderName"></param>
		/// <param name="listFields"></param>
		/// <param name="doc"></param>
		/// <returns></returns>
		public static bool PutDocuments(string webUrl, string sharepointFolderName,ArrayList listFields,Document doc)
		{


			string requestToUrl = null;
			bool returnValue=true;
			requestToUrl = webUrl + sFPExtensionPath;	
			HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestToUrl);
			if(TimeOut>0)
				request.Timeout=TimeOut;

			string mimeBoundary = "--MIMEboundary-" + DateTime.UtcNow.ToString("u"); //"--mime boundary-";
			request.Credentials  = CredentialCache.DefaultCredentials;
			request.Method = "POST";
			request.KeepAlive = true; 
			request.Accept = "auth/sicily";
			request.Headers.Add("MIME-Version","1.0"); 
			request.ContentType = "multipart/mixed; boundary=\"" + mimeBoundary.Substring(2)  + "\""; 
			request.Headers.Add("X-Vermeer-Content-Type","multipart/mixed; boundary=\"" + mimeBoundary.Substring(2) + "\""); 

			#region Data To Post
			MemoryStream dataToPost = new MemoryStream();
 
			//Method specification
			StringBuilder postData = new StringBuilder();
			postData.Append(mimeBoundary + "\r\n");
			postData.Append("Content-Type: application/x-www-form-urlencoded" + "\r\n" + "\r\n");
			postData.Append("method=put documents:" + GetServerExtensionsVersion(webUrl));
			postData.Append("&service_name=" + "webServerRelativeUrl");
			postData.Append("&put_option=overwrite,migrationsemantics&listFiles=true&listLinkInfo=true");
			postData.Append("\n"); 
			
			//Write data to memory stream
			byte[] postDataBytes = System.Text.Encoding.ASCII.GetBytes(postData.ToString ());
			dataToPost.Write(postDataBytes,0,postDataBytes.Length); 
			int byteCount = postDataBytes.Length; 

			string documentData;
			try
			{
				IEnumerator enumerator = doc.Versions;
				while(enumerator.MoveNext())
				{
					//Get document data
					documentData = GetDocumentRequestString((DocumentVersion)enumerator.Current,sharepointFolderName,mimeBoundary,listFields); 
					postDataBytes = System.Text.Encoding.ASCII.GetBytes(documentData);

					//Write document data to memory stream
					dataToPost.Write(postDataBytes,0,postDataBytes.Length);
					byteCount += postDataBytes.Length; 

					//Get File contents
					postDataBytes = ((DocumentVersion)enumerator.Current).ReadContent(); 

					//Write file contents to memory stream
					dataToPost.Write(postDataBytes,0,postDataBytes.Length);
					byteCount += postDataBytes.Length;

					//Write newline at end of file contents
					postDataBytes = System.Text.Encoding.ASCII.GetBytes("\n");
					dataToPost.Write(postDataBytes,0,postDataBytes.Length);
					byteCount += postDataBytes.Length;
				}
			}
			catch(IOException Ex)
			{
				Logger.Log(ErrorLevel.Error,"The enumeration of the versions failed. Error: "+Ex.Message);
			}


			
			//Get document data
			
			//Document information
			documentData = GetDocumentRequestString(doc,sharepointFolderName,mimeBoundary,listFields); 
			postDataBytes = System.Text.Encoding.ASCII.GetBytes(documentData);

			//Write document data to memory stream
			dataToPost.Write(postDataBytes,0,postDataBytes.Length);
			byteCount += postDataBytes.Length; 

			//Get File contents
			postDataBytes = doc.ReadContent(); 
			if(null==postDataBytes)
				return false;

			//Write file contents to memory stream
			dataToPost.Write(postDataBytes,0,postDataBytes.Length);
			byteCount += postDataBytes.Length;

			//Get End Data in Post request
			string endData = "\n" + "\r\n" + mimeBoundary + "--";
			postDataBytes = System.Text.Encoding.ASCII.GetBytes(endData);

			//Write end data to memory stream
			dataToPost.Write(postDataBytes,0,postDataBytes.Length);
			byteCount += postDataBytes.Length;

			//dataToPost.Flush(); 
			#endregion

			request.ContentLength = byteCount;

			

			Stream requestStream = null;
			try
			{
				int BUF_SIZE = 32768;

				//Write the post data to request stream
				requestStream = request.GetRequestStream() ;
				byte[] buffer = new byte[BUF_SIZE]; 
				int bytesRead = 0;
				dataToPost.Seek(0,SeekOrigin.Begin);  
				while(0 < (bytesRead = dataToPost.Read(buffer,0,BUF_SIZE)))
				{
					requestStream.Write(buffer,0,bytesRead);
				}

				//Send the request and get response
				HttpWebResponse response = (HttpWebResponse)request.GetResponse ();
				
				//Read the response
				using (Stream responseStream = response.GetResponseStream())
				{

					using (StreamReader reader = new StreamReader(responseStream))
					{
						string strResponse = reader.ReadToEnd(); 
						//Check response to see if request was successful
						if(!ValidatePutDocumentsCall(strResponse))
						{
							Logger.Log(ErrorLevel.Error,"Could not upload the document "+doc.Name+". Response from server: "+strResponse); 
							returnValue= false;
						}
					}
 

				}
			}
			catch(WebException WebEx)
			{
				if(WebEx.Status==WebExceptionStatus.Timeout)
					Logger.Log(ErrorLevel.Error,"HTTP request timed out when uploading document"+doc.Name);
				else
					Logger.Log(ErrorLevel.Error,"WebException occurred in PutDocuments. Error:"+ WebEx.Message);
				returnValue=false;
			}
			catch(IOException Ex)
			{
				Logger.Log(ErrorLevel.Error,"Exception occurred in PutDocuments. Error:"+ Ex.Message);
				returnValue=false;
			}
			finally
			{
				if(null != dataToPost) dataToPost.Close(); 
				if(null != requestStream) requestStream.Close();
			}

			return returnValue;
		}

		//
		/// <summary>
		/// Checks the response to see if the put documents call was successful or not.
		/// </summary>
		/// <param name="response"></param>
		/// <returns></returns>
		static private bool ValidatePutDocumentsCall(string response)
		{
			if(-1 != response.IndexOf("method") && -1 != response.IndexOf("docs") && -1 != response.IndexOf("message"))
			{
				return true;
			}
			return false;
		}


	}
}
